﻿using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SocketApp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        Socket clinetSocket = null;
        // 包长
        int packetSize = 10;
        private void button1_Click(object sender, EventArgs e)
        {
            clinetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);
            try
            {
                clinetSocket.Connect(endPoint);
                Action action = ReceiveMsg;
                Task.Run(action);//开启线程监听来自服务器消息
                if (clinetSocket.Connected)
                {
                    MessageBox.Show("Connected");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
        /*
         * 1. 固定长度分包:直到接收完整个数据包(只适合固定包长度的场景)
         * 2. 在数据包中添加分隔符:接收方在接收数据时，根据分隔符将数据包分割成多个数据包。(和服务端沟通使用的分隔符)
         * 3. 在数据包中添加长度信息:先接收数据包头部的长度信息，然后根据长度信息接收数据包
         */
        private void ReceiveMsg()
        {
            while (true)
            {
                byte[] buffer = new byte[5 * 1024];
                int length = clinetSocket.Receive(buffer);
                if (length > 0)
                {
                    byte[] temp = new byte[length];
                    Array.Copy(buffer, 0, temp, 0, length);

                    #region 常规方式,可能会黏包
                    //this.Invoke(new Action(() =>
                    //    listRevMsg.Items.Add(Encoding.ASCII.GetString(temp))
                    //));
                    #endregion


                    ////1. 固定长度分包:直到接收完整个数据包(只适合固定包长度的场景)
                    //if (temp.Length == packetSize)
                    //{
                    //    this.Invoke(new Action(() =>
                    //    listRevMsg.Items.Add(Encoding.ASCII.GetString(temp))
                    //  ));
                    //}

                    //// 2. 在数据包中添加分隔符:接收方在接收数据时，根据分隔符将数据包分割成多个数据包。(和服务端沟通使用的分隔符)
                    //byte[] separator = Encoding.UTF8.GetBytes("|");
                    //// 获取到分隔符的索引,这里的分隔符是:"|"
                    //int index = Array.IndexOf(buffer, separator[0], 0, length);
                    //if (index >= 0)
                    //{
                    //    //
                    //    this.Invoke(new Action(() =>
                    //    {
                    //        var data1 = Encoding.ASCII.GetString(temp);
                    //        var res = data1.Substring(0, index);
                    //        listRevMsg.Items.Add(res);
                    //    }));
                    //}

                    //3. 在数据包中添加长度信息:先接收数据包头部的长度信息，然后根据长度信息接收数据包
                    // 假设报文5ABCDE,数据长度为5,取出的数据位ABCDE
                    var data = GetData(temp);
                    if (data != null)
                    {
                        this.Invoke(new Action(() =>
                        listRevMsg.Items.Add(data)
                     ));
                    }
                }
            }
        }

        private string GetData(byte[] lstArr)
        {
            var allres = Encoding.ASCII.GetString(lstArr);
            // 假设一包中第一位为数据长度,这里获取数据长度
            int count = int.Parse(allres[0].ToString());
            // 根据数据长度取数据
            string res = new string(allres.Skip(1).Take(count).ToArray());
            return res;
        }
    }
}
